package cn.iocoder.yudao.module.search.service.engine.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.search.api.search.dto.*;
import cn.iocoder.yudao.module.search.framework.config.SearchProperties;
import cn.iocoder.yudao.module.search.service.engine.SearchEngine;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;

import java.nio.charset.StandardCharsets;
import java.util.*;

import static cn.iocoder.yudao.module.search.enums.ErrorCodeConstants.*;

/**
 * ZincSearch 搜索引擎实现
 *
 * @author 芋道源码
 */
@Slf4j
@RequiredArgsConstructor
public class ZincSearchEngineImpl implements SearchEngine {

    private final SearchProperties.ZincSearchConfig config;
    private final WebClient webClient;
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 获取配置的 WebClient
     */
    private WebClient getWebClient() {
        String credentials = config.getUsername() + ":" + config.getPassword();
        String encodedCredentials = Base64.getEncoder()
                .encodeToString(credentials.getBytes(StandardCharsets.UTF_8));

        return webClient.mutate()
                .baseUrl(config.getUrl())
                .defaultHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedCredentials)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }

    // ========== 索引管理 ==========

    @Override
    public Boolean createIndex(SearchIndexReqDTO reqDTO) {
        try {
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("name", reqDTO.getIndexName());
            requestBody.put("storage_type", "disk");
            requestBody.put("shard_num", 1);

            if (reqDTO.getMapping() != null) {
                requestBody.put("mappings", reqDTO.getMapping());
            }
            if (reqDTO.getSettings() != null) {
                requestBody.put("settings", reqDTO.getSettings());
            }

            String response = getWebClient()
                    .post()
                    .uri("/api/index")
                    .bodyValue(requestBody)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            log.info("[createIndex][创建索引成功] indexName: {}, response: {}", reqDTO.getIndexName(), response);
            return true;

        } catch (WebClientResponseException e) {
            log.error("[createIndex][创建索引失败] indexName: {}, status: {}, response: {}", 
                    reqDTO.getIndexName(), e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(INDEX_CREATE_FAIL.getCode(), 
                    "创建索引失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[createIndex][创建索引异常] indexName: {}", reqDTO.getIndexName(), e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "创建索引异常: " + e.getMessage());
        }
    }

    @Override
    public Boolean deleteIndex(String indexName) {
        try {
            String response = getWebClient()
                    .delete()
                    .uri("/api/index/{indexName}", indexName)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            log.info("[deleteIndex][删除索引成功] indexName: {}, response: {}", indexName, response);
            return true;

        } catch (WebClientResponseException e) {
            if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
                log.warn("[deleteIndex][索引不存在] indexName: {}", indexName);
                return true; // 索引不存在也认为删除成功
            }
            log.error("[deleteIndex][删除索引失败] indexName: {}, status: {}, response: {}", 
                    indexName, e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(INDEX_DELETE_FAIL.getCode(), 
                    "删除索引失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[deleteIndex][删除索引异常] indexName: {}", indexName, e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "删除索引异常: " + e.getMessage());
        }
    }

    @Override
    public Boolean existsIndex(String indexName) {
        try {
            getWebClient()
                    .get()
                    .uri("/api/index/{indexName}", indexName)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            return true;

        } catch (WebClientResponseException e) {
            if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
                return false;
            }
            log.error("[existsIndex][检查索引存在性失败] indexName: {}, status: {}, response: {}", 
                    indexName, e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "检查索引存在性失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[existsIndex][检查索引存在性异常] indexName: {}", indexName, e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "检查索引存在性异常: " + e.getMessage());
        }
    }

    @Override
    public SearchIndexRespDTO getIndex(String indexName) {
        try {
            String response = getWebClient()
                    .get()
                    .uri("/api/index/{indexName}", indexName)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            JsonNode jsonNode = objectMapper.readTree(response);
            SearchIndexRespDTO respDTO = new SearchIndexRespDTO();
            respDTO.setIndexName(indexName);
            
            if (jsonNode.has("mappings")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> mappingMap = objectMapper.convertValue(jsonNode.get("mappings"), Map.class);
                respDTO.setMapping(mappingMap);
            }
            if (jsonNode.has("settings")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> settingsMap = objectMapper.convertValue(jsonNode.get("settings"), Map.class);
                respDTO.setSettings(settingsMap);
            }
            if (jsonNode.has("doc_num")) {
                respDTO.setDocumentCount(jsonNode.get("doc_num").asLong());
            }
            if (jsonNode.has("storage_size")) {
                respDTO.setIndexSize(jsonNode.get("storage_size").asLong());
            }

            return respDTO;

        } catch (WebClientResponseException e) {
            if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new ServiceException(INDEX_NOT_EXISTS.getCode(), "索引不存在: " + indexName);
            }
            log.error("[getIndex][获取索引信息失败] indexName: {}, status: {}, response: {}", 
                    indexName, e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "获取索引信息失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[getIndex][获取索引信息异常] indexName: {}", indexName, e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "获取索引信息异常: " + e.getMessage());
        }
    }

    @Override
    public List<SearchIndexRespDTO> listIndexes() {
        try {
            String response = getWebClient()
                    .get()
                    .uri("/api/index")
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            JsonNode jsonNode = objectMapper.readTree(response);
            List<SearchIndexRespDTO> result = new ArrayList<>();

            if (jsonNode.isArray()) {
                for (JsonNode indexNode : jsonNode) {
                    SearchIndexRespDTO respDTO = new SearchIndexRespDTO();
                    if (indexNode.has("name")) {
                        respDTO.setIndexName(indexNode.get("name").asText());
                    }
                    if (indexNode.has("doc_num")) {
                        respDTO.setDocumentCount(indexNode.get("doc_num").asLong());
                    }
                    if (indexNode.has("storage_size")) {
                        respDTO.setIndexSize(indexNode.get("storage_size").asLong());
                    }
                    result.add(respDTO);
                }
            }

            return result;

        } catch (Exception e) {
            log.error("[listIndexes][获取索引列表异常]", e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(), 
                    "获取索引列表异常: " + e.getMessage());
        }
    }

    // ========== 文档管理 ==========

    @Override
    public String createDocument(SearchDocumentReqDTO reqDTO) {
        try {
            String response;
            if (StrUtil.isNotBlank(reqDTO.getDocumentId())) {
                response = getWebClient()
                        .post()
                        .uri("/api/{indexName}/_doc/{documentId}", reqDTO.getIndexName(), reqDTO.getDocumentId())
                        .bodyValue(reqDTO.getDocument())
                        .retrieve()
                        .bodyToMono(String.class)
                        .timeout(config.getReadTimeout())
                        .block();
            } else {
                response = getWebClient()
                        .post()
                        .uri("/api/{indexName}/_doc", reqDTO.getIndexName())
                        .bodyValue(reqDTO.getDocument())
                        .retrieve()
                        .bodyToMono(String.class)
                        .timeout(config.getReadTimeout())
                        .block();
            }

            JsonNode jsonNode = objectMapper.readTree(response);
            String documentId = jsonNode.has("_id") ? jsonNode.get("_id").asText() : reqDTO.getDocumentId();

            log.info("[createDocument][创建文档成功] indexName: {}, documentId: {}",
                    reqDTO.getIndexName(), documentId);
            return documentId;

        } catch (WebClientResponseException e) {
            log.error("[createDocument][创建文档失败] indexName: {}, documentId: {}, status: {}, response: {}",
                    reqDTO.getIndexName(), reqDTO.getDocumentId(), e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(DOCUMENT_CREATE_FAIL.getCode(),
                    "创建文档失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[createDocument][创建文档异常] indexName: {}, documentId: {}",
                    reqDTO.getIndexName(), reqDTO.getDocumentId(), e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "创建文档异常: " + e.getMessage());
        }
    }

    @Override
    public Boolean updateDocument(SearchDocumentReqDTO reqDTO) {
        try {
            if (StrUtil.isBlank(reqDTO.getDocumentId())) {
                throw new ServiceException(DOCUMENT_UPDATE_FAIL.getCode(), "更新文档时文档ID不能为空");
            }

            getWebClient()
                    .put()
                    .uri("/api/{indexName}/_doc/{documentId}", reqDTO.getIndexName(), reqDTO.getDocumentId())
                    .bodyValue(reqDTO.getDocument())
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            log.info("[updateDocument][更新文档成功] indexName: {}, documentId: {}",
                    reqDTO.getIndexName(), reqDTO.getDocumentId());
            return true;

        } catch (WebClientResponseException e) {
            log.error("[updateDocument][更新文档失败] indexName: {}, documentId: {}, status: {}, response: {}",
                    reqDTO.getIndexName(), reqDTO.getDocumentId(), e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(DOCUMENT_UPDATE_FAIL.getCode(),
                    "更新文档失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[updateDocument][更新文档异常] indexName: {}, documentId: {}",
                    reqDTO.getIndexName(), reqDTO.getDocumentId(), e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "更新文档异常: " + e.getMessage());
        }
    }

    @Override
    public Boolean deleteDocument(String indexName, String documentId) {
        try {
            getWebClient()
                    .delete()
                    .uri("/api/{indexName}/_doc/{documentId}", indexName, documentId)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            log.info("[deleteDocument][删除文档成功] indexName: {}, documentId: {}", indexName, documentId);
            return true;

        } catch (WebClientResponseException e) {
            if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
                log.warn("[deleteDocument][文档不存在] indexName: {}, documentId: {}", indexName, documentId);
                return true; // 文档不存在也认为删除成功
            }
            log.error("[deleteDocument][删除文档失败] indexName: {}, documentId: {}, status: {}, response: {}",
                    indexName, documentId, e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(DOCUMENT_DELETE_FAIL.getCode(),
                    "删除文档失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[deleteDocument][删除文档异常] indexName: {}, documentId: {}", indexName, documentId, e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "删除文档异常: " + e.getMessage());
        }
    }

    @Override
    public SearchDocumentRespDTO getDocument(String indexName, String documentId) {
        try {
            String response = getWebClient()
                    .get()
                    .uri("/api/{indexName}/_doc/{documentId}", indexName, documentId)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            JsonNode jsonNode = objectMapper.readTree(response);
            SearchDocumentRespDTO respDTO = new SearchDocumentRespDTO();
            respDTO.setIndexName(indexName);
            respDTO.setDocumentId(documentId);

            if (jsonNode.has("_source")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> sourceMap = objectMapper.convertValue(jsonNode.get("_source"), Map.class);
                respDTO.setDocument(sourceMap);
            }
            if (jsonNode.has("_score")) {
                respDTO.setScore(jsonNode.get("_score").asDouble());
            }

            return respDTO;

        } catch (WebClientResponseException e) {
            if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new ServiceException(DOCUMENT_NOT_EXISTS.getCode(),
                        "文档不存在: " + indexName + "/" + documentId);
            }
            log.error("[getDocument][获取文档失败] indexName: {}, documentId: {}, status: {}, response: {}",
                    indexName, documentId, e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "获取文档失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[getDocument][获取文档异常] indexName: {}, documentId: {}", indexName, documentId, e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "获取文档异常: " + e.getMessage());
        }
    }

    @Override
    public SearchBulkRespDTO bulkDocument(SearchBulkReqDTO reqDTO) {
        try {
            List<Map<String, Object>> bulkData = new ArrayList<>();

            for (SearchBulkReqDTO.BulkOperation operation : reqDTO.getOperations()) {
                Map<String, Object> actionMap = new HashMap<>();
                Map<String, Object> actionBody = new HashMap<>();
                actionBody.put("_index", reqDTO.getIndexName());

                if (StrUtil.isNotBlank(operation.getDocumentId())) {
                    actionBody.put("_id", operation.getDocumentId());
                }

                actionMap.put(operation.getOperation(), actionBody);
                bulkData.add(actionMap);

                if (!"delete".equals(operation.getOperation()) && operation.getDocument() != null) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> documentMap = (Map<String, Object>) operation.getDocument();
                    bulkData.add(documentMap);
                }
            }

            String response = getWebClient()
                    .post()
                    .uri("/api/_bulk")
                    .bodyValue(bulkData)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            JsonNode jsonNode = objectMapper.readTree(response);
            SearchBulkRespDTO respDTO = new SearchBulkRespDTO();

            if (jsonNode.has("took")) {
                respDTO.setTook(jsonNode.get("took").asLong());
            }
            if (jsonNode.has("errors")) {
                respDTO.setErrors(jsonNode.get("errors").asBoolean());
            }

            List<SearchBulkRespDTO.BulkOperationResult> results = new ArrayList<>();
            if (jsonNode.has("items") && jsonNode.get("items").isArray()) {
                for (JsonNode itemNode : jsonNode.get("items")) {
                    SearchBulkRespDTO.BulkOperationResult result = new SearchBulkRespDTO.BulkOperationResult();

                    // 获取操作类型和结果
                    @SuppressWarnings("deprecation")
                    Iterator<Map.Entry<String, JsonNode>> fields = itemNode.fields();
                    while (fields.hasNext()) {
                        Map.Entry<String, JsonNode> entry = fields.next();
                        result.setOperation(entry.getKey());
                        JsonNode operationResult = entry.getValue();

                        if (operationResult.has("_id")) {
                            result.setDocumentId(operationResult.get("_id").asText());
                        }
                        if (operationResult.has("status")) {
                            result.setStatus(operationResult.get("status").asInt());
                            result.setSuccess(operationResult.get("status").asInt() < 400);
                        }
                        if (operationResult.has("error")) {
                            result.setError(operationResult.get("error").asText());
                            result.setSuccess(false);
                        }
                        break; // 只处理第一个字段
                    }

                    results.add(result);
                }
            }
            respDTO.setResults(results);

            log.info("[bulkDocument][批量操作文档成功] indexName: {}, operations: {}",
                    reqDTO.getIndexName(), reqDTO.getOperations().size());
            return respDTO;

        } catch (WebClientResponseException e) {
            log.error("[bulkDocument][批量操作文档失败] indexName: {}, status: {}, response: {}",
                    reqDTO.getIndexName(), e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(DOCUMENT_BULK_FAIL.getCode(),
                    "批量操作文档失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[bulkDocument][批量操作文档异常] indexName: {}", reqDTO.getIndexName(), e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "批量操作文档异常: " + e.getMessage());
        }
    }

    // ========== 搜索查询 ==========

    @Override
    public SearchQueryRespDTO search(SearchQueryReqDTO reqDTO) {
        try {
            Map<String, Object> searchBody = buildSearchQuery(reqDTO);

            String response = getWebClient()
                    .post()
                    .uri("/api/{indexName}/_search", reqDTO.getIndexName())
                    .bodyValue(searchBody)
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(config.getReadTimeout())
                    .block();

            return parseSearchResponse(response);

        } catch (WebClientResponseException e) {
            log.error("[search][搜索查询失败] indexName: {}, query: {}, status: {}, response: {}",
                    reqDTO.getIndexName(), reqDTO.getQuery(), e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException(SEARCH_EXECUTE_FAIL.getCode(),
                    "搜索查询失败: " + e.getResponseBodyAsString());
        } catch (Exception e) {
            log.error("[search][搜索查询异常] indexName: {}, query: {}", reqDTO.getIndexName(), reqDTO.getQuery(), e);
            throw new ServiceException(SEARCH_ENGINE_OPERATION_ERROR.getCode(),
                    "搜索查询异常: " + e.getMessage());
        }
    }

    /**
     * 构建搜索查询体
     */
    private Map<String, Object> buildSearchQuery(SearchQueryReqDTO reqDTO) {
        Map<String, Object> searchBody = new HashMap<>();

        // 设置搜索类型，默认为 match
        searchBody.put("search_type", "match");

        // 构建查询条件
        Map<String, Object> query = new HashMap<>();
        if (StrUtil.isNotBlank(reqDTO.getQuery())) {
            query.put("term", reqDTO.getQuery());
        }

        // 设置查询字段
        if (CollUtil.isNotEmpty(reqDTO.getFields())) {
            query.put("field", String.join(",", reqDTO.getFields()));
        } else {
            query.put("field", "_all");
        }

        searchBody.put("query", query);

        // 设置分页
        int from = (reqDTO.getPageNo() - 1) * reqDTO.getPageSize();
        searchBody.put("from", from);
        searchBody.put("max_results", reqDTO.getPageSize());

        // 设置排序
        if (CollUtil.isNotEmpty(reqDTO.getSorts())) {
            List<String> sortFields = new ArrayList<>();
            for (Map<String, String> sort : reqDTO.getSorts()) {
                for (Map.Entry<String, String> entry : sort.entrySet()) {
                    String field = entry.getKey();
                    String order = entry.getValue();
                    if ("desc".equalsIgnoreCase(order)) {
                        field = "-" + field;
                    }
                    sortFields.add(field);
                }
            }
            searchBody.put("sort_fields", sortFields);
        }

        // 设置返回字段
        if (CollUtil.isNotEmpty(reqDTO.getHighlightFields())) {
            searchBody.put("_source", reqDTO.getHighlightFields());
        } else {
            searchBody.put("_source", Collections.emptyList());
        }

        return searchBody;
    }

    /**
     * 解析搜索响应
     */
    private SearchQueryRespDTO parseSearchResponse(String response) {
        try {
            JsonNode jsonNode = objectMapper.readTree(response);
            SearchQueryRespDTO respDTO = new SearchQueryRespDTO();

            // 设置耗时
            if (jsonNode.has("took")) {
                respDTO.setTook(jsonNode.get("took").asLong());
            }

            // 设置最大评分
            if (jsonNode.has("max_score")) {
                respDTO.setMaxScore(jsonNode.get("max_score").asDouble());
            }

            // 解析命中结果
            if (jsonNode.has("hits")) {
                JsonNode hitsNode = jsonNode.get("hits");

                // 设置总数
                if (hitsNode.has("total")) {
                    JsonNode totalNode = hitsNode.get("total");
                    if (totalNode.has("value")) {
                        respDTO.setTotal(totalNode.get("value").asLong());
                    } else {
                        respDTO.setTotal(totalNode.asLong());
                    }
                }

                // 解析文档列表
                if (hitsNode.has("hits") && hitsNode.get("hits").isArray()) {
                    List<SearchDocumentRespDTO> documents = new ArrayList<>();
                    for (JsonNode hitNode : hitsNode.get("hits")) {
                        SearchDocumentRespDTO docDTO = new SearchDocumentRespDTO();

                        if (hitNode.has("_index")) {
                            docDTO.setIndexName(hitNode.get("_index").asText());
                        }
                        if (hitNode.has("_id")) {
                            docDTO.setDocumentId(hitNode.get("_id").asText());
                        }
                        if (hitNode.has("_score")) {
                            docDTO.setScore(hitNode.get("_score").asDouble());
                        }
                        if (hitNode.has("_source")) {
                            @SuppressWarnings("unchecked")
                            Map<String, Object> sourceMap = objectMapper.convertValue(hitNode.get("_source"), Map.class);
                            docDTO.setDocument(sourceMap);
                        }

                        documents.add(docDTO);
                    }
                    respDTO.setDocuments(documents);
                }
            }

            // 解析聚合结果
            if (jsonNode.has("buckets")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> aggregationsMap = objectMapper.convertValue(jsonNode.get("buckets"), Map.class);
                respDTO.setAggregations(aggregationsMap);
            }

            return respDTO;

        } catch (Exception e) {
            log.error("[parseSearchResponse][解析搜索响应异常] response: {}", response, e);
            throw new ServiceException(SEARCH_RESULT_PARSE_FAIL.getCode(),
                    "解析搜索响应异常: " + e.getMessage());
        }
    }

}
